#pragma once

#include <atlctrls.h>
#include <atlrx.h>
#include <string>
#include <map>
#include <algorithm>

#include "HttpStreamHandler.h"
#include "CURLHelper.h"

#define LINE_TCHAR_BUFFER		512		//Define the message buffer size
#define TOTAL_EDIT_MAX_CHARS	65535	//Edit Control Max chars
#define POST_URL_MAX_LENGTH		128		//MAX Length URL

#define WHITE_COLOR	 			RGB(0xff, 0xff, 0xff)
#define BLACK_COLOR	 			RGB(0x00, 0x00, 0x00)
#define DARK_BLUE_COLOR		 	RGB(0x22, 0x99, 0xaf)
#define LIGHT_CRAY_COLOR		RGB(0xee, 0xee, 0xf2)
#define LIGHT_CRAY_COLOR2		RGB(0xff, 0xff, 0xfb)

#define LIST_ITEM_HEIGHT		20		//18pix
#define LIST_IMAGE_HEIGHT		16		//16pix
#define LIST_IMAGE_X_OFFSET		5		//5pix
#define LIST_IMAGE_Y_OFFSET		((LIST_ITEM_HEIGHT - LIST_IMAGE_HEIGHT)/2)	//16pix image, so set 1 to center the image
#define LIST_TEXT_X_OFFSET		(2*LIST_IMAGE_X_OFFSET + LIST_IMAGE_HEIGHT)

#define SNIFFER_ITEM_PHASE				6
#define SNIFFER_PHASE_START				1
#define SNIFFER_PHASE_HTTPOK			2
#define SNIFFER_PHASE_GETGZIP			3
#define SNIFFER_PHASE_GETPLAIN			3
#define SNIFFER_PHASE_DECODESTREAMOK	4
#define SNIFFER_PHASE_SNIFFERBEGIN		5
#define SNIFFER_PHASE_SNIFFEROK			6
#define SNIFFER_PHASE_SNIFFERZERO		6
#define SNIFFER_PHASE_HTTPFAIL			6
#define SNIFFER_PHASE_GZIPDECERROR		6

using std::wstring;
using std::string;


class CTaskList : public COwnerDraw<CTaskList>,
				  public CWindowImpl<CTaskList, CListBox>
{
public:
	typedef enum _URL_STATUS {
		S_WAIT = 0,
		S_PROCESSING,
		S_TIMEOUT,
		S_ERROR,
		S_NOITEM,
		S_FINISH
	} URL_STATUS;

	typedef struct _URL_ITEM_DATA {
		URL_STATUS urlStatus;
		UINT uiItemSize;
	} URL_ITEM_DATA;

public:
	BEGIN_MSG_MAP(CEditURLInput)
		CHAIN_MSG_MAP_ALT(COwnerDraw<CTaskList>, 1)		//Must be the ALT 1, cause the diag reflect the msg to the listbox
		MSG_OCM_CTLCOLORLISTBOX(OnListBkColor)			//Yes, it's also a reflected message

		DEFAULT_REFLECTION_HANDLER()					//Reflect other msg to the control
	END_MSG_MAP()

//Now do the owner draw, I really hate this
public:
	void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
	{
		CDCHandle dc = lpDrawItemStruct->hDC;
		CRect rect = lpDrawItemStruct->rcItem;

		//This callback is used frequently, so stack buffer will be more efficient
		WCHAR lpwstrItemURL[POST_URL_MAX_LENGTH] = { 0 };
		WCHAR lpwstrItemInfo[LINE_TCHAR_BUFFER] = { 0 };
		WCHAR lpwstrItemFormat[LINE_TCHAR_BUFFER] = { 0 };

		GetText(lpDrawItemStruct->itemID, lpwstrItemURL);
		GetText(lpDrawItemStruct->itemID, lpwstrItemInfo);
		
		if((lpDrawItemStruct->itemState & ODS_SELECTED)  == ODS_SELECTED)
		{
			dc.FillSolidRect(&lpDrawItemStruct->rcItem, DARK_BLUE_COLOR);
			dc.SetTextColor(WHITE_COLOR);
		}
		else	//To get a better apearence
		{
			if(lpDrawItemStruct->itemID % 2 == 0)
			{
				dc.FillSolidRect(&lpDrawItemStruct->rcItem, LIGHT_CRAY_COLOR);
			}
			else
			{
				dc.FillSolidRect(&lpDrawItemStruct->rcItem, LIGHT_CRAY_COLOR2);
			}

			dc.SetTextColor(BLACK_COLOR);		
		}

		dc.SetBkMode(TRANSPARENT);

		CDC mdc;
		mdc.CreateCompatibleDC(dc);

		CBitmapHandle hPreBmp;
		URL_ITEM_DATA enStatus = *(URL_ITEM_DATA*)(lpDrawItemStruct->itemData);
		
		switch(enStatus.urlStatus)
		{
		case S_WAIT:
			hPreBmp = mdc.SelectBitmap(hbmWait);
			break;

		case S_PROCESSING:
			hPreBmp = mdc.SelectBitmap(hbmProccesing);
			break;

		case S_NOITEM:
			::LoadStringW(::GetModuleHandle(NULL), IDS_TA_LIST_NO_ITEM, lpwstrItemFormat, LINE_TCHAR_BUFFER);
			::wsprintfW(lpwstrItemInfo, lpwstrItemFormat, lpwstrItemURL);
			hPreBmp = mdc.SelectBitmap(hbmNoitem);
			break;

		case S_TIMEOUT:
			::LoadStringW(::GetModuleHandle(NULL), IDS_TA_LIST_TIMEOUT_FORMAT, lpwstrItemFormat, LINE_TCHAR_BUFFER);
			::wsprintfW(lpwstrItemInfo, lpwstrItemFormat, lpwstrItemURL);
			hPreBmp = mdc.SelectBitmap(hbmError);
			break;

		case S_ERROR:
			::LoadStringW(::GetModuleHandle(NULL), IDS_TA_LIST_ERROR_FORMAT, lpwstrItemFormat, LINE_TCHAR_BUFFER);
			::wsprintfW(lpwstrItemInfo, lpwstrItemFormat, lpwstrItemURL);
			hPreBmp = mdc.SelectBitmap(hbmError);
			break;

		case S_FINISH:
			::LoadStringW(::GetModuleHandle(NULL), IDS_TA_LIST_FINISH_FORMAT, lpwstrItemFormat, LINE_TCHAR_BUFFER);
			::wsprintfW(lpwstrItemInfo, lpwstrItemFormat, lpwstrItemURL, enStatus.uiItemSize);
			hPreBmp = mdc.SelectBitmap(hbmFinish);
			break;
		}

		//Draw the status bmp
		dc.AlphaBlend(rect.left + LIST_IMAGE_X_OFFSET, rect.top + LIST_IMAGE_Y_OFFSET, LIST_IMAGE_HEIGHT, LIST_IMAGE_HEIGHT, mdc, 0, 0, LIST_IMAGE_HEIGHT, LIST_IMAGE_HEIGHT, alphaBlendFunction);

		//Restore GDI resource
		mdc.SelectBitmap(hPreBmp);

		rect.left += LIST_TEXT_X_OFFSET;
		dc.DrawTextW(lpwstrItemInfo, ::lstrlenW(lpwstrItemInfo), rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE);

	}

	void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
	{
		lpMeasureItemStruct->itemHeight = m_uiItemHeight;
	}

	LRESULT OnListBkColor(HDC hDC, HWND hWnd)
	{
		return (LRESULT)hbkBrush.m_hBrush;
	}

public:
	CTaskList()
	{
		hbmError.LoadBitmapW(IDB_BM_TASKERROR);
		hbmFinish.LoadBitmapW(IDB_BM_TASKFINISH);
		hbmProccesing.LoadBitmapW(IDB_BM_TASKPROCESSING);
		hbmWait.LoadBitmapW(IDB_BM_TASKWAIT);
		hbmNoitem.LoadBitmapW(IDB_BM_TASKNOITEM);

		hbkBrush.CreateSolidBrush(LIGHT_CRAY_COLOR);

		alphaBlendFunction.BlendOp = AC_SRC_OVER;
		alphaBlendFunction.BlendFlags = 0;
		alphaBlendFunction.AlphaFormat = AC_SRC_ALPHA;
		alphaBlendFunction.SourceConstantAlpha = 255;	//Our bitmap is 32bit with alpha chanel
	}

	void AjustListHeight()
	{
		//The list box need more 4 pix for a owner draw virable to fit height with no blank
		CRect rectList;
		GetWindowRect(&rectList);
		if((rectList.Height() % m_uiItemHeight) != 4)
		{
			rectList.bottom += (4 - (rectList.Height() % m_uiItemHeight));
		}

		::MapWindowPoints(HWND_DESKTOP, GetParent(), (LPPOINT)&rectList, 2);
		MoveWindow(&rectList, FALSE);
	}

public:
	CBitmap hbmError;
	CBitmap hbmFinish;
	CBitmap hbmProccesing;
	CBitmap hbmWait;
	CBitmap hbmNoitem;
	CBrush	hbkBrush;

	BLENDFUNCTION alphaBlendFunction;
	UINT m_uiItemHeight;
};

class CEditURLInput : public CWindowImpl<CEditURLInput, CEdit>
{
//Message Handler
public:
	BEGIN_MSG_MAP(CEditURLInput)
		MESSAGE_HANDLER(WM_KEYDOWN, OnCheckSelAll)
	END_MSG_MAP()

	LRESULT OnCheckSelAll(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		if((TCHAR)wParam == L'A' && GetKeyState(VK_CONTROL) < 0)
		{   
			SetSelAll();
			//To remove the boring beep from Edit control, becasue it does not rec this shortcut
			//This is the only way I have known
			MSG msg;
			::PeekMessage(&msg, *this, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE);
			return TRUE;
		}
		
		//We Just handle ctrl+A
		bHandled = false;
		return FALSE;
	}

//Control Helper
public:
	int GetNoLineBreakStringFromInput(wstring& wstrInputText)
	{
		int nCharCount = 0;

		for(int i=0; i < GetLineCount(); i++)
		{
			LPWSTR lpwstrLine = new WCHAR[LINE_TCHAR_BUFFER];
			::memset(lpwstrLine, 0, LINE_TCHAR_BUFFER);
			nCharCount += GetLine(i, lpwstrLine, LINE_TCHAR_BUFFER);

			wstrInputText += lpwstrLine;
			delete lpwstrLine;
		}

		return nCharCount;
	}

	int GetInputCharCount()
	{
		int nCharCount = 0;

		for(int i=0; i < GetLineCount(); i++)
		{
			LPWSTR lpwstrLine = new WCHAR[LINE_TCHAR_BUFFER];
			::memset(lpwstrLine, 0, LINE_TCHAR_BUFFER);
			nCharCount += GetLine(i, lpwstrLine, LINE_TCHAR_BUFFER);

			delete lpwstrLine;
		}

		return nCharCount;
	}
};

//Because the this is a modal box, system will start the private message loop, so no pretranslate needed
class CSnifferURLDiag : public CDialogImpl<CSnifferURLDiag>,
						public CDialogResize<CSnifferURLDiag>
{
public:
	enum { IDD = IDD_SNIFFER_URL };

	//For easy use of Curl item status
	typedef CCURLHelper::HTTP_ITEMS_SNIFFER::SNIFFER_STATUS CURL_SNIFFER_STATUS;
	typedef CCURLHelper::HTTP_ITEMS_SNIFFER					CURL_SNIFFER;

	BEGIN_MSG_MAP(CSnifferURLDiag)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

		//Handling multithreading
		MESSAGE_HANDLER(WM_HTTP_ITEM_RESULT_FWD, OnItemResult)

		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
		COMMAND_HANDLER(IDC_EDIT_URLINPUT, EN_CHANGE, OnEditChange)

		CHAIN_MSG_MAP(CDialogResize<CSnifferURLDiag>)

		REFLECT_NOTIFICATIONS()	//Reflect other msg to the control self, so we can use owner draw
	END_MSG_MAP()

	BEGIN_DLGRESIZE_MAP(CSnifferURLDiag)
		DLGRESIZE_CONTROL(IDOK, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(IDCANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(IDC_EDIT_URLINPUT, DLSZ_SIZE_X | DLSZ_SIZE_Y)
		DLGRESIZE_CONTROL(IDC_LIST_TASK, DLSZ_SIZE_X | DLSZ_SIZE_Y)
		
		DLGRESIZE_CONTROL(IDC_STATIC_CURRENT_PROGRESS_TITLE, DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(IDC_STATIC_TOTALPROGRESS_TITLE, DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(IDC_STATIC_CURRENT_INFO, DLSZ_SIZE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(IDC_STATIC_TOTAL_INFO, DLSZ_SIZE_X | DLSZ_MOVE_Y)

		DLGRESIZE_CONTROL(IDC_PROGRESS_TOTAL_URL, DLSZ_SIZE_X | DLSZ_MOVE_Y)
		DLGRESIZE_CONTROL(IDC_PROGRESS_CUR_URL, DLSZ_SIZE_X | DLSZ_MOVE_Y)
	END_DLGRESIZE_MAP()

public:
	CSnifferURLDiag();
	~CSnifferURLDiag();
	void CloseDialog(int nVal);
	void EnterProcessing(bool isEnter);
	void SetCurrentProgressAndInfo(CURL_SNIFFER_STATUS cssItemStatus);
	void SetTotalProgressAndInfo(UINT uiCurrentIndex, CURL_SNIFFER_STATUS cssItemStatus);
	UINT ExtractURLFromInput(LPCWSTR lpcwstrInput, LPCWSTR lpcwstrRegex, vector<string>& vecstrURLs, vector<wstring>& vecwstrURLs);
	void ProcessingURLs();
	static LRESULT CALLBACK ThreadMsgHook(int nCode, WPARAM wParam, LPARAM lParam);			//Msg hook function

public:
	//Message Handler
	LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
	LRESULT OnItemResult(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

	LRESULT OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
	LRESULT OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);

	LRESULT OnEditChange(UINT uiCode, UINT cid, HWND hWndControl, BOOL& bHandled);
	
public:
	CEditURLInput m_edtURL;
	CProgressBarCtrl m_pgbCurrent, m_pgbTotal;
	CButton m_btAnalyze, m_btCancel;
	CStatic m_stcTotalProgress, m_stcCurrentProgress;
	CTaskList m_lstTask;

	bool m_bIsProcessing;
	UINT m_uiTotalItems;
	vector<string> m_vecstrJSUrls;
	vector<wstring> m_vecwstrJSUrls;
	map<string, vector<CHttpStreamHandler::JSItem>> m_mapJSItems;		//This is the one we pass out to other modules
	map<string, string> m_mapJSSecIp;									//To Hold SecIP for a page to generate link
	CTaskList::URL_ITEM_DATA* m_pArrURLItemData;
	CCURLHelper::HTTP_ITEMS_SNIFFER* m_phtiParam;						//Thread param

	//Used by threading message hooking
	static HHOOK m_hHook;
	static HWND m_sDiagWnd;
	static bool m_bIsHooked;
};
